﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

namespace CloudShot.Controls
{
  public class AnimatedGifPlayer : Control
  {
    public event Action<float> ProgressChanged;

    private readonly Timer _timer = new Timer();

    private int _frameCount;
    private int _currentFrame = -1;
    private Image _image;

    public Image Image
    {
      get { return _image; }
      set
      {
        _currentFrame = -1;

        if (value != null)
        {
          _image = MakeImageCopy(value);
          _frameCount = _image.GetFrameCount(FrameDimension.Time); //total frames in the animation    
        }
        else
        {
          _image = null;
          _frameCount = 0;
        }

        InvokeProgressChanged();
      }
    }

    public bool IsPlaying
    {
      get { return _timer.Enabled; }
    }

    public AnimatedGifPlayer()
    {
      //necessary to prevent flicker
      SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

      _timer.Tick += OnTimerTick;
      _timer.Enabled = false;
    }

    public void Play()
    {
      _timer.Interval = GetFrameDelay(_image);
      _timer.Start();
    }

    public void Pause()
    {
      _timer.Stop();
      Invalidate();
    }

    public void Stop()
    {
      Pause();
      _currentFrame = -1;
      InvokeProgressChanged();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      base.OnPaint(e);

      if (_image != null)
      {
        e.Graphics.DrawImage(_image, Point.Empty);
      }

      Rectangle rect = ClientRectangle;
      rect.Width -= 1;
      rect.Height -= 1;
      e.Graphics.DrawRectangle(Pens.Black, rect);
    }

    private void OnTimerTick(object sender, EventArgs e)
    {
      GoToNextFrame();
      Invalidate();
    }

    private static Image MakeImageCopy(Image image)
    {
      //copy image as animated gif
      var ms = new MemoryStream();
      image.Save(ms, ImageFormat.Gif);

      //http://stackoverflow.com/questions/3845456/loading-an-image-from-a-stream-without-keeping-the-stream-open
      //NOTE: According to the documentation of Image.FromStream the stream must be kept open while the image is in use. http://support.microsoft.com/kb/814675
      return Image.FromStream(ms);
    }

    private static int GetFrameDelay(Image image)
    {
      if (image == null)
        return -1;

      int delay = 200;

      try
      {
        PropertyItem item = image.GetPropertyItem(0x5100); // FrameDelay in libgdiplus
        // Time is in 1/100th of a second
        delay = (item.Value[0] + item.Value[1] * 256) * 10;
      }
      catch { }

      return delay;
    }

    private void GoToNextFrame()
    {
      _currentFrame++;

      //if the animation reaches a boundary...
      if (_currentFrame >= _frameCount || _currentFrame < 1)
        _currentFrame = 0;

      _image.SelectActiveFrame(FrameDimension.Time, _currentFrame); //find the frame

      InvokeProgressChanged();
    }

    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);

      if (disposing && _timer != null)
        _timer.Stop();

      _image = null;
    }

    protected virtual void InvokeProgressChanged()
    {
      float progress = 0;

      if (_currentFrame >= 0 && _frameCount > 0)
      {
        progress = ((float)_currentFrame + 1) / _frameCount;
      }

      Action<float> handler = ProgressChanged;
      if (handler != null)
        handler(progress);
    }
  }
}